home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / Astro / astrolog / Source / xgeneral.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  39.5 KB  |  1,431 lines

  1. /*
  2. ** Astrolog (Version 4.10) File: xgeneral.c
  3. **
  4. ** IMPORTANT NOTICE: the graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1994 by Walter D. Pullen
  6. ** (cruiser1@stein.u.washington.edu). Permission is granted to freely
  7. ** use and distribute these routines provided one doesn't sell,
  8. ** restrict, or profit from them in any way. Modification is allowed
  9. ** provided these notices remain with any altered or edited versions of
  10. ** the program.
  11. **
  12. ** The main planetary calculation routines used in this program have
  13. ** been Copyrighted and the core of this program is basically a
  14. ** conversion to C of the routines created by James Neely as listed in
  15. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  16. ** available from Matrix Software. The copyright gives us permission to
  17. ** use the routines for personal use but not to sell them or profit from
  18. ** them in any way.
  19. **
  20. ** The PostScript code within the core graphics routines are programmed
  21. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  22. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  23. **
  24. ** The extended accurate ephemeris databases and formulas are from the
  25. ** calculation routines in the program "Placalc" and are programmed and
  26. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  27. ** (alois@azur.ch). The use of that source code is subject to
  28. ** regulations made by Astrodienst Zurich, and the code is not in the
  29. ** public domain. This copyright notice must not be changed or removed
  30. ** by any user of this program.
  31. **
  32. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  33. ** X Window graphics initially programmed 10/23-29/1991.
  34. ** PostScript graphics initially programmed 11/29-30/1992.
  35. ** Last code change made 3/19/1994.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40. #ifdef GRAPH
  41.  
  42. int xpen, ypen;
  43.  
  44.  
  45. /*
  46. ******************************************************************************
  47. ** Bitmap File Routines.
  48. ******************************************************************************
  49. */
  50.  
  51. /* Write the bitmap array to a previously opened file in a format that   */
  52. /* can be read in by the Unix X commands bitmap and xsetroot. The 'mode' */
  53. /* parameter defines how much white space is put in the file.            */
  54.  
  55. void WriteXBitmap(data, name, mode)
  56. FILE *data;
  57. char *name, mode;
  58. {
  59.   int x, y, i, temp = 0;
  60.   _int value;
  61.  
  62.   fprintf(data, "#define %s_width %d\n" , name, chartx);
  63.   fprintf(data, "#define %s_height %d\n", name, charty);
  64.   fprintf(data, "static %s %s_bits[] = {",
  65.     mode != 'V' ? "char" : "short", name);
  66.   for (y = 0; y < charty; y++) {
  67.     x = 0;
  68.     do {
  69.  
  70.       /* Process each row, eight columns at a time. */
  71.  
  72.       if (y + x > 0)
  73.         fprintf(data, ",");
  74.       if (temp == 0)
  75.         fprintf(data, "\n%s",
  76.           mode == 'N' ? "  " : (mode == 'C' ? " " : ""));
  77.       value = 0;
  78.       for (i = (mode != 'V' ? 7 : 15); i >= 0; i--)
  79.         value = (value << 1) +
  80.           (!(PGET(bm, x+i, y)^(xreverse*15))^xreverse && (x + i < chartx));
  81.       if (mode == 'N')
  82.         putc(' ', data);
  83.       fprintf(data, "0x");
  84.       if (mode == 'V')
  85.         fprintf(data, "%c%c",
  86.           INTTOHEX(value >> 12), INTTOHEX((value >> 8) & 15));
  87.       fprintf(data, "%c%c",
  88.         INTTOHEX((value >> 4) & 15), INTTOHEX(value & 15));
  89.       temp++;
  90.  
  91.       /* Is it time to skip to the next line while writing the file yet? */
  92.  
  93.       if ((mode == 'N' && temp >= 12) ||
  94.           (mode == 'C' && temp >= 15) ||
  95.           (mode == 'V' && temp >= 11))
  96.         temp = 0;
  97.       x += (mode != 'V' ? 8 : 16);
  98.     } while (x < chartx);
  99.   }
  100.   fprintf(data, "};\n");
  101. }
  102.  
  103.  
  104. /* Write the bitmap array to a previously opened file in a simple boolean    */
  105. /* Ascii rectangle, one char per pixel, where '#' represents an off bit and  */
  106. /* '-' an on bit. The output format is identical to the format generated by  */
  107. /* the Unix bmtoa command, and it can be converted into a bitmap with atobm. */
  108.  
  109. void WriteAscii(data)
  110. FILE *data;
  111. {
  112.   int x, y, i;
  113.  
  114.   for (y = 0; y < charty; y++) {
  115.     for (x = 0; x < chartx; x++) {
  116.       i = PGET(bm, x, y);
  117.       if (xcolor)
  118.         putc(INTTOHEX(i), data);
  119.       else
  120.         putc(i ? '-' : '#', data);
  121.     }
  122.     putc('\n', data);
  123.   }
  124. }
  125.  
  126.  
  127. /* Write the bitmap array to a previously opened file in the bitmap format  */
  128. /* used in Microsoft Windows for its .bmp extension files. This is a pretty */
  129. /* efficient format, only requiring one bit per pixel and a small header.   */
  130.  
  131. void WriteBmp(data)
  132. FILE *data;
  133. {
  134.   int x, y;
  135.   dword value;
  136.  
  137.   /* BitmapFileHeader */
  138.   PutByte('B'); PutByte('M');
  139.   PutLong(14+40 + (xcolor ? 64 : 8) +
  140.     (long)4*charty*((chartx-1 >> (xcolor ? 3 : 5))+1));
  141.   PutWord(0); PutWord(0);
  142.   PutLong(14+40 + (xcolor ? 64 : 8));
  143.   /* BitmapInfo / BitmapInfoHeader */
  144.   PutLong(40);
  145.   PutLong(chartx); PutLong(charty);
  146.   PutWord(1); PutWord(xcolor ? 4 : 1);
  147.   PutLong(0 /*BI_RGB*/); PutLong(0);
  148.   PutLong(0); PutLong(0);
  149.   PutLong(0); PutLong(0);
  150.   /* RgbQuad */
  151.   if (xcolor)
  152.     for (x = 0; x < 16; x++) {
  153.       PutByte(RGBB(rgbbmp[x])); PutByte(RGBG(rgbbmp[x]));
  154.       PutByte(RGBR(rgbbmp[x])); PutByte(0);
  155.     }
  156.   else {
  157.     PutLong(0);
  158.     PutByte(255); PutByte(255); PutByte(255); PutByte(0);
  159.   }
  160.   /* Data */
  161.   for (y = charty-1; y >= 0; y--) {
  162.     value = 0;
  163.     for (x = 0; x < chartx; x++) {
  164.       if ((x & (xcolor ? 7 : 31)) == 0 && x > 0) {
  165.         PutLong(value);
  166.         value = 0;
  167.       }
  168.       if (xcolor)
  169.         value |= (dword)PGET(bm, x, y) << ((x & 7 ^ 1) << 2);
  170.       else
  171.         if (PGET(bm, x, y))
  172.           value |= (dword)1 << (x & 31 ^ 7);
  173.     }
  174.     PutLong(value);
  175.   }
  176. }
  177.  
  178.  
  179. /* Output the bitmap in memory to a file. This basically consists of just    */
  180. /* calling some routine to actually write a bitmap to a file, although we    */
  181. /* need to prompt the user for a filename if it wasn't specified beforehand. */
  182.  
  183. void WriteFile()
  184. {
  185.   char line[STRING];
  186.   FILE *data;
  187.  
  188. #ifdef PS
  189.   if (psfile) {
  190.       PSend();
  191.     return;
  192.   }
  193. #endif
  194.   if (outputfile == NULL && (metafile || (xbitmap && bitmapmode == 'B')))
  195.     fprintf(stdout, "(It is recommended to specify an extension of '.%s'.)\n",
  196.       xbitmap ? "bmp" : "wmf");
  197.   loop {
  198.     if (outputfile == NULL) {
  199.       sprintf(line, "Enter name of file to write %s to",
  200.         xbitmap ? "bitmap" : "metafile");
  201.       InputString(line, line);
  202.       outputfile = line;
  203.     }
  204.     data = fopen(outputfile, "wb");
  205.     if (data != NULL)
  206.       break;
  207.     else {
  208.       PrintWarning("Couldn't create output file.");
  209.       outputfile = NULL;
  210.     }
  211.   }
  212.   if (xbitmap) {
  213.     if (bitmapmode == 'B')
  214.       WriteBmp(data);
  215.     else if (bitmapmode == 'A')
  216.       WriteAscii(data);
  217.     else
  218.       WriteXBitmap(data, outputfile, bitmapmode);
  219.   }
  220. #ifdef META
  221.   else
  222.     WriteMeta(data);
  223. #endif
  224.   fclose(data);
  225. }
  226.  
  227.  
  228. /*
  229. ******************************************************************************
  230. ** PostScript File Routines.
  231. ******************************************************************************
  232. */
  233.  
  234. #ifdef PS
  235.  
  236. /* Global variables used by the PostScript generator. */
  237.  
  238. FILE *psdata;
  239. int strokecount = 0, currentlinecap = 0, currentdash = 0, currentfont = 0;
  240. real currentlinewidth = 1.0;
  241.  
  242. /* Table of PostScript header alias lines used by the program. */
  243.  
  244. char PSfunctions[] =
  245. "/languagelevel where{pop languagelevel}{1}ifelse\
  246.  2 lt{\n\
  247. /sf{exch findfont exch\
  248.  dup type/arraytype eq{makefont}{scalefont}ifelse setfont}bind def\n\
  249. /rf{gsave newpath\n\
  250. 4 -2 roll moveto\
  251.  dup 0 exch rlineto exch 0 rlineto neg 0 exch rlineto closepath\n\
  252. fill grestore}bind def\n\
  253. /rc{newpath\n\
  254. 4 -2 roll moveto\
  255.  dup 0 exch rlineto exch 0 rlineto neg 0 exch rlineto closepath\n\
  256. clip newpath}bind def\n\
  257. }{/sf/selectfont load def/rf/rectfill load def\
  258. /rc/rectclip load def}ifelse\n\
  259. /center{0 begin gsave dup 4 2 roll\
  260.  translate newpath 0 0 moveto\
  261.  false charpath flattenpath pathbbox\
  262.  /URy exch def/URx exch def/LLy exch def/LLx exch def\
  263.  URx LLx sub 0.5 mul LLx add neg URy LLy sub 0.5 mul LLy add neg\
  264.  0 0 moveto rmoveto\
  265.  show grestore end}bind def\n\
  266. /center load 0 4 dict put\n\
  267. /c{setrgbcolor}bind def\n\
  268. /d{moveto 0 0 rlineto}bind def\n\
  269. /l{4 2 roll moveto lineto}bind def\n\
  270. /t{lineto}bind def\n\
  271. /el{newpath matrix currentmatrix 5 1 roll translate scale\
  272.  0 0 1 0 360 arc setmatrix stroke}bind def\n";
  273.  
  274.  
  275. /* Write a command to flush the PostScript buffer. */
  276.  
  277. void PSforcestroke()
  278. {
  279.   if (strokecount > 0) {            /* render any existing path */
  280.     fprintf(psdata, "stroke\n");
  281.     strokecount = 0;
  282.     xpen = -1;                      /* Invalidate PolyLine cache */
  283.   }
  284. }
  285.  
  286.  
  287. /* Indicate that a certain number of PostScript commands have been done. */
  288.  
  289. void PSstroke(n)
  290. int n;
  291. {
  292.   strokecount += n;
  293.   if (strokecount > 5000)    /* Whenever we reach a certain limit, flush. */
  294.     PSforcestroke();
  295. }
  296.  
  297.  
  298. /* Set the type of line end to be used by PostScript commands. If linecap */
  299. /* is true, then the line ends are rounded, otherwise they are squared.   */
  300.  
  301. void PSlinecap(linecap)
  302. int linecap;
  303. {
  304.   if (linecap != currentlinecap) {
  305.     PSforcestroke();
  306.     fprintf(psdata, "%d setlinecap\n", linecap);
  307.     currentlinecap = linecap;
  308.   }
  309. }
  310.  
  311.  
  312. /* Set the dash length to be used by PostScript line commands. */
  313.  
  314. void PSdash(dashoff)
  315. int dashoff;
  316. {
  317.   if (dashoff != currentdash) {
  318.     PSforcestroke();
  319.     if (dashoff)
  320.       fprintf(psdata, "[%d %d", PSMUL, dashoff * PSMUL);
  321.     else
  322.       fprintf(psdata, "[");
  323.     fprintf(psdata, "]0 setdash\n");
  324.     currentdash = dashoff;
  325.   }
  326. }
  327.  
  328.  
  329. /* Set a linewidth size to be used by PostScript figure primitive commands. */
  330.  
  331. void PSlinewidth(linewidth)
  332. int linewidth;
  333. {
  334.   if (linewidth != currentlinewidth) {
  335.     PSforcestroke();
  336.     fprintf(psdata, "%d setlinewidth\n", linewidth);
  337.     currentlinewidth = linewidth;
  338.   }
  339. }
  340.  
  341.  
  342. /* Set a system font and size to be used by PostScript text commands. */
  343.  
  344. void PSfont(psfont)
  345. int psfont;
  346. {
  347.   int temp;
  348.  
  349.   if (psfont != currentfont && xfont) {
  350.     if (psfont <= 2) {
  351.       temp = psfont == 1 ? 32*PSMUL : 23*PSMUL;
  352.         fprintf(psdata, "/Astro[%d 0 0 -%d 0 0]sf\n", temp, temp);
  353.     } else if (psfont == 3) {
  354.       temp = 26*PSMUL;
  355.       fprintf(psdata, "/Times-Roman[%d 0 0 -%d 0 0]sf\n", temp, temp);
  356.     } else {
  357.       temp = 10*PSMUL;
  358.       fprintf(psdata, "/Courier[%d 0 0 -%d 0 0]sf\n", temp, temp);
  359.     }
  360.     currentfont = psfont;
  361.   }
  362. }
  363.  
  364.  
  365. /* Prompt the user for the name of a file to write the PostScript file to */
  366. /* (if not already specified), open it, and write out file header info.   */
  367.  
  368. void PSbegin()
  369. {
  370.   char line[STRING];
  371.  
  372.   if (outputfile == NULL && epsfile)
  373.     fprintf(stdout,
  374.       "(It is recommended to specify an extension of '.eps'.)\n");
  375.   loop {
  376.     if (outputfile == NULL) {
  377.       sprintf(line, "Enter name of file to write PostScript to");
  378.       InputString(line, line);
  379.       outputfile = line;
  380.     }
  381.     psdata = fopen(outputfile, "w");
  382.     if (psdata != NULL)
  383.       break;
  384.     else {
  385.       PrintWarning("Couldn't create output file.");
  386.       outputfile = NULL;
  387.     }
  388.   }
  389.   fprintf(psdata, "%%!PS-Adobe-2.0");
  390.   if (epsfile)
  391.     fprintf(psdata, " EPSF-2.0");
  392.   fprintf(psdata, "\n%%%%Title: %s\n", outputfile);
  393.   fprintf(psdata, "%%%%Creator: %s %s\n", appname, VERSION);
  394.   fprintf(psdata, "%%%%CreationDate: %s\n", DATE);
  395.   if (epsfile) {
  396.     fprintf(psdata, "%%%%BoundingBox: 0 0 %d %d\n", chartx, charty);
  397.     fprintf(psdata, "%%%%EndComments\n");
  398.     fprintf(psdata, "%%%%BeginSetup\n");
  399.     fprintf(psdata, PSfunctions, 6 * PSMUL, 6 * PSMUL);
  400.     fprintf(psdata, "%%%%EndSetup\n");
  401.     fprintf(psdata, "0 0 %d %d rc\n", chartx, charty);
  402.   } else {
  403.     fprintf(psdata, "%%%%Pages: 1 1\n");
  404.     fprintf(psdata, "%%%%DocumentFonts: (atend)\n");
  405.     fprintf(psdata, "%%%%BoundingBox: 9 9 %d %d\n",
  406.       (int)(psinchx*72.0+ROUND)-9, (int)(psinchy*72.0+ROUND)-9);
  407.     fprintf(psdata, "%%%%EndComments\n");
  408.     fprintf(psdata, "%%%%BeginProcSet: common\n");
  409.     fprintf(psdata, PSfunctions, 6 * PSMUL, 6 * PSMUL);
  410.     fprintf(psdata, "%%%%EndProcSet\n");
  411.     fprintf(psdata, "%%%%Page: 1 1\n");
  412.   }
  413.   PSfont(2);
  414.   fprintf(psdata, "gsave\n");
  415.   PSlinewidth(metawid/2);
  416.   xpen = -1;
  417. }
  418.  
  419.  
  420. /* Write out trailing information to the PostScript file and close it. */
  421.  
  422. void PSend()
  423. {
  424.   PSforcestroke();
  425.   if (epsfile)
  426.     fprintf(psdata, "%%%%EOF\n");
  427.   else {
  428.     fprintf(psdata, "showpage\n");
  429.     fprintf(psdata, "%%%%PageTrailer\n");
  430.     fprintf(psdata, "%%%%Trailer\n");
  431.     fprintf(psdata, "%%%%DocumentFonts: Times-Roman\n");
  432.     if (xfont) {
  433.       fprintf(psdata, "%%%%+ Courier\n");
  434.       fprintf(psdata, "%%%%+ Astro\n");
  435.     }
  436.   }
  437.   fclose(psdata);
  438. }
  439. #endif /* PS */
  440.  
  441.  
  442. /*
  443. ******************************************************************************
  444. ** Metafile Routines.
  445. ******************************************************************************
  446. */
  447.  
  448. #ifdef META
  449.  
  450. /* Global variables used by the metafile generator. */
  451.  
  452. colpal metalinedes, metalineact = -1,    /* Desired and actual line color. */
  453.   metafilldes,      metafillact = -1,    /* Desired and actual fill color. */
  454.   metafontdes = -1, metafontact = -1,    /* Desired and actual text font.  */
  455.   metatxtcdes = -1, metatxtcact = -1,    /* Desired and actual text color. */
  456.   metatxtades = -1, metatxtaact = -1;    /* Desired/actual text alignment. */
  457.  
  458. /* Macros to output the various metafile commands we use. */
  459.  
  460. #define MetaRecord(S, R) MetaLong((long)(S)); MetaWord(R)
  461. #define MetaSelectObject(O) MetaRecord(4, 0x12D); MetaWord(O)
  462. #define MetaDeleteObject(O) MetaRecord(4, 0x1F0); MetaWord(O)
  463. #define MetaSaveDc() MetaRecord(3, 0x01E)
  464. #define MetaRestoreDc() MetaRecord(4, 0x127); MetaWord(-1)
  465. #define MetaWindowOrg(X, Y) MetaRecord(5, 0x20B); MetaWord(Y); MetaWord(X)
  466. #define MetaWindowExt(X, Y) MetaRecord(5, 0x20C); MetaWord(Y); MetaWord(X)
  467. #define MetaCreatePen(S, W, C) MetaRecord(8, 0x2FA); MetaWord(S); \
  468.   MetaWord(W); MetaWord(W); MetaLong(C)
  469. #define MetaCreateBrush(S, C) MetaRecord(7, 0x2FC); \
  470.   MetaWord(S); MetaLong(C); MetaWord(0 /* Not used */);
  471. #define MetaCreateFont(S, X, Y, C) MetaRecord(12+(S), 0x2FB); MetaWord(Y); \
  472.   MetaWord(X); MetaWord(0 /* Angle */); MetaWord(0 /* Not used */); \
  473.   MetaWord(400 /* Normal Weight */); MetaWord(0 /* Italic, Underline */); \
  474.   MetaWord(MAKEWORD(0 /* Strikeout */, C)); \
  475.   MetaWord(MAKEWORD(4 /* TrueType */, 0 /* Clip */))
  476. #define MetaBkMode(M) MetaRecord(4, 0x102); MetaWord(M)
  477. #define MetaTextAlign(A) MetaRecord(4, 0x12E); MetaWord(A)
  478. #define MetaTextColor(C) MetaRecord(5, 0x209); MetaLong(C);
  479. #define MetaTextOut(X, Y, S) MetaRecord(7+((S)+1)/2, 0xA32); \
  480.   MetaWord(Y); MetaWord(X); MetaWord(S); MetaWord(0 /* ETO */)
  481. #define MetaRectangle(X1, Y1, X2, Y2) MetaRecord(7, 0x41B); \
  482.   MetaWord(Y2); MetaWord(X2); MetaWord(Y1); MetaWord(X1)
  483. #define MetaEllipse(X1, Y1, X2, Y2) MetaRecord(7, 0x418); \
  484.   MetaWord(Y2); MetaWord(X2); MetaWord(Y1); MetaWord(X1)
  485. #define MetaEscape(S) MetaRecord((S), 0x626); \
  486.   MetaWord(15 /* MFCOMMENT */); MetaWord(((S)-5)*2 /* Bytes in comment */);
  487.  
  488.  
  489. /* Output one 16 bit or 32 bit value into the metafile buffer stream. */
  490.  
  491. void MetaWord(w)
  492. word w;
  493. {
  494.   char string[STRING];
  495.  
  496.   if ((byte PTR)metacur - bm >= MAXMETA) {
  497.     sprintf(string, "Metafile would be more than %ld bytes.", MAXMETA);
  498.     PrintError(string);
  499.     Terminate(_FATAL);
  500.   }
  501.   *metacur = w;
  502.   metacur++;
  503. }
  504.  
  505. void MetaLong(l)
  506. long l;
  507. {
  508.   MetaWord(LOWORD(l));
  509.   MetaWord(HIWORD(l));
  510. }
  511.  
  512.  
  513. /* Output any necessary metafile records to make the current actual     */
  514. /* settings of line color, fill color, etc, be those that we know are   */
  515. /* desired. This is generally called by the primitives routines before  */
  516. /* any figure record is actually written into a metafile. We wait until */
  517. /* the last moment before changing any settings to ensure that we don't */
  518. /* output any unnecessary records, e.g. two select colors in a row.     */
  519.  
  520. void MetaSelect()
  521. {
  522.   if (metalinedes != metalineact) {
  523.     MetaSelectObject(metalinedes);
  524.     metalineact = metalinedes;
  525.   }
  526.   if (metafilldes != metafillact) {
  527.     MetaSelectObject(16*4 + metafilldes);
  528.     metafillact = metafilldes;
  529.   }
  530.   if (metafontdes != metafontact) {
  531.     MetaSelectObject(16*5 + metafontdes);
  532.     metafontact = metafontdes;
  533.   }
  534.   if (metatxtcdes != metatxtcact) {
  535.     MetaTextColor(rgbbmp[metatxtcdes]);
  536.     metatxtcact = metatxtcdes;
  537.   }
  538.   if (metatxtades != metatxtaact) {
  539.     MetaTextAlign(metatxtades);
  540.     metatxtaact = metatxtades;
  541.   }
  542.   xpen = -1;    /* Invalidate PolyLine cache */
  543. }
  544.  
  545.  
  546. /* Output initial metafile header information into our metafile buffer. */
  547. /* We also setup and create all pen, brush, and font objects that may   */
  548. /* possibly be used in the generation and playing of the picture.       */
  549.  
  550. void MetaInit()
  551. {
  552.   int i, j, k;
  553.  
  554.   metacur = (word PTR)bm;
  555.   /* Placable Metaheader */
  556.   MetaLong(0x9AC6CDD7L);
  557.   MetaWord(0);      /* Not used */
  558.   MetaWord(0); MetaWord(0);
  559.   MetaWord(chartx); MetaWord(charty);
  560.   MetaWord(chartx/6);  /* Units per inch */
  561.   MetaLong(0L);     /* Not used */
  562.   MetaWord(0x9AC6 ^ 0xCDD7 ^ chartx ^ charty ^ chartx/6);  /* Checksum */
  563.   /* Metaheader */
  564.   MetaWord(1);                   /* Metafile type */
  565.   MetaWord(9);                   /* Size of header in words */
  566.   MetaWord(0x300);               /* Windows version */
  567.   MetaLong(0L);                  /* Size of entire metafile in words */
  568.   MetaWord(16*5+1+(xfont>0)*4);  /* Number of objects in metafile */
  569.   MetaLong(17L);                 /* Size of largest record in words */
  570.   MetaWord(0);                   /* Not used */
  571.   /* Setup */
  572.   MetaEscape(17);
  573.   MetaLong(MAKEQUAD('A', 's', 't', 'r'));  /* "Astr" */
  574.   MetaWord(4);                             /* Creator */
  575.   MetaLong(14L);                           /* Bytes in string */
  576.   MetaLong(MAKEQUAD('A', 's', 't', 'r'));  /* "Astr" */
  577.   MetaLong(MAKEQUAD('o', 'l', 'o', 'g'));  /* "olog" */
  578.   MetaLong(MAKEQUAD(' ', '4', '.', '1'));  /* " 4.1" */
  579.   MetaWord(MAKEWORD('0', 0));              /* "0"    */
  580.   MetaSaveDc();
  581.   MetaWindowOrg(0, 0);
  582.   MetaWindowExt(chartx, charty);
  583.   MetaBkMode(1 /* Transparent */);
  584.   /* Colors */
  585.   for (j = 1; j <= 4; j++)
  586.     for (i = 0; i < 16; i++) {
  587.       k = j <= 1 ? metawid : 0;
  588.       MetaCreatePen(j <= 2 ? 0 : j-2 /* PS_SOLID; PS_DASH; PS_DOT */,
  589.         k, rgbbmp[i]);
  590.     }
  591.   for (i = 0; i < 16; i++) {
  592.     MetaCreateBrush(0 /* BS_SOLID */, rgbbmp[i]);
  593.   }
  594.   MetaCreateBrush(1 /* BS_NULL */, 0L);
  595.   /* Fonts */
  596.   if (xfont) {
  597.     MetaCreateFont(5, 0, -8*SCALE, 2 /* Symbol Charset */);
  598.     MetaWord(MAKEWORD(1 /* Draft */, 1 | 0x10 /* Fixed | Roman */));
  599.     MetaLong(MAKEQUAD('W', 'i', 'n', 'g'));
  600.     MetaLong(MAKEQUAD('d', 'i', 'n', 'g'));
  601.     MetaWord(MAKEWORD('s', 0));
  602.  
  603.     MetaCreateFont(8, 0, -6*SCALE, 0 /* Ansi Charset */);
  604.     MetaWord(MAKEWORD(0 /* Default */, 2 | 0x10 /* Variable | Roman */));
  605.     MetaLong(MAKEQUAD('T', 'i', 'm', 'e'));
  606.     MetaLong(MAKEQUAD('s', ' ', 'N', 'e'));
  607.     MetaLong(MAKEQUAD('w', ' ', 'R', 'o'));
  608.     MetaLong(MAKEQUAD('m', 'a', 'n', 0));
  609.  
  610.     MetaCreateFont(6, 6*METAMUL, 10*METAMUL, 0 /* Ansi Charset */);
  611.     MetaWord(MAKEWORD(1 /* Draft */, 1 | 0x30 /* Fixed | Modern */));
  612.     MetaLong(MAKEQUAD('C', 'o', 'u', 'r'));
  613.     MetaLong(MAKEQUAD('i', 'e', 'r', ' '));
  614.     MetaLong(MAKEQUAD('N', 'e', 'w', 0));
  615.  
  616.     MetaCreateFont(8, 0, -11*SCALE, 0 /* Ansi Charset */);
  617.     MetaWord(MAKEWORD(0 /* Default */, 2 | 0 /* Variable | Don't Care */));
  618.     MetaLong(MAKEQUAD('A', 's', 't', 'r'));
  619.     MetaLong(MAKEQUAD('o', '-', 'S', 'e'));
  620.     MetaLong(MAKEQUAD('m', 'i', 'B', 'o'));
  621.     MetaLong(MAKEQUAD('l', 'd', 0, 0));
  622.   }
  623. }
  624.  
  625.  
  626. /* Output trailing records to indicate the end of the metafile and then */
  627. /* actually write out the entire buffer to the specifed file.           */
  628.  
  629. void WriteMeta(data)
  630. FILE *data;
  631. {
  632.   word PTR w;
  633. #if FALSE
  634.   int i;
  635.  
  636.   for (i = 16*5+1+(xfont>0)*4; i >= 0; i--) {
  637.     MetaDeleteObject(i);
  638.   }
  639. #endif
  640.   MetaRestoreDc();
  641.   MetaRecord(3, NULL);    /* End record */
  642.   *(long PTR)(bm + 22 + 6) = ((long)((byte PTR)metacur - bm) - 22) / 2;
  643.   for (w = (word PTR)bm; w < metacur; w++) {
  644.     PutWord(*w);
  645.   }
  646. }
  647. #endif /* META */
  648.  
  649.  
  650. /*
  651. ******************************************************************************
  652. ** Core Graphic Procedures.
  653. ******************************************************************************
  654. */
  655.  
  656. /* Set the current color to use in drawing on the screen or bitmap array. */
  657.  
  658. void DrawColor(col)
  659. colpal col;
  660. {
  661.   if (xfile) {
  662. #ifdef PS
  663.     if (psfile) {
  664.       if (colcur != col) {
  665.         PSforcestroke();      /* Render existing path with current color */
  666.         fprintf(psdata, "%.2f %.2f %.2f c\n",
  667.           (real)RGBR(rgbbmp[col])/255.0, (real)RGBG(rgbbmp[col])/255.0,
  668.           (real)RGBB(rgbbmp[col])/255.0);
  669.       }
  670.     }
  671. #endif
  672. #ifdef META
  673.     if (metafile)
  674.       metalinedes = col;
  675. #endif
  676.   }
  677. #ifdef X11
  678.   else
  679.     XSetForeground(disp, gc, rgbind[col]);
  680. #endif
  681. #ifdef MSG
  682.   else
  683.     _setcolor(col);
  684. #endif
  685.   colcur = col;
  686. }
  687.  
  688.  
  689. /* Set a single point on the screen. This is the most basic graphic function */
  690. /* and is called by all the more complex routines. Based on what mode we are */
  691. /* in, we either set a cell in the bitmap array or a pixel on the window.    */
  692.  
  693. void DrawPoint(x, y)
  694. int x, y;
  695. {
  696.   if (xfile) {
  697.     if (xbitmap) {
  698.       /* Force the coordinates to be within the bounds of the bitmap array. */
  699.  
  700.       if (x < 0)
  701.         x = 0;
  702.       else if (x >= chartx)
  703.         x = chartx-1;
  704.       if (y < 0)
  705.         y = 0;
  706.       else if (y >= charty)
  707.         y = charty-1;
  708.       PSET(bm, x, y, colcur);
  709.     }
  710. #ifdef PS
  711.     else if (psfile) {
  712.       DrawColor(colcur);
  713.       PSlinecap(TRUE);
  714.       fprintf(psdata, "%d %d d\n", x, y);
  715.       PSstroke(2);
  716.     }
  717. #endif
  718. #ifdef META
  719.     else {
  720.       metafilldes = colcur;
  721.       MetaSelect();
  722.       MetaEllipse(x-metawid/2, y-metawid/2, x+metawid/2, y+metawid/2);
  723.     }
  724. #endif
  725.   }
  726. #ifdef X11
  727.   else
  728.     XDrawPoint(disp, pixmap, gc, x, y);
  729. #endif
  730. #ifdef MSG
  731.   else
  732.     _setpixel(offsetx + x, offsety + y);
  733. #endif
  734. }
  735.  
  736.  
  737. /* Draw dot a little larger than just a single pixel at specified location. */
  738.  
  739. void DrawSpot(x, y)
  740. int x, y;
  741. {
  742. #ifdef PS
  743.   if (psfile) {
  744.     PSlinewidth(currentlinewidth*3);
  745.     DrawPoint(x, y);
  746.     PSlinewidth(currentlinewidth/3);
  747.     return;
  748.   }
  749. #endif
  750. #ifdef META
  751.   if (metafile) {
  752.     metafilldes = colcur;
  753.     MetaSelect();
  754.     MetaEllipse(x-metawid, y-metawid, x+metawid, y+metawid);
  755.     return;
  756.   }
  757. #endif
  758.   DrawPoint(x, y);
  759.   DrawPoint(x, y-1);
  760.   DrawPoint(x-1, y);
  761.   DrawPoint(x+1, y);
  762.   DrawPoint(x, y+1);
  763. }
  764.  
  765.  
  766. /* Draw a filled in block, defined by the corners of its rectangle. */
  767.  
  768. void DrawBlock(x1, y1, x2, y2)
  769. int x1, y1, x2, y2;
  770. {
  771.   int x, y;
  772.  
  773.   if (xfile) {
  774.     if (xbitmap) {
  775.       for (y = y1; y <= y2; y++)         /* For bitmap, we have to  */
  776.         for (x = x1; x <= x2; x++)       /* just fill in the array. */
  777.           PSET(bm, x, y, colcur);
  778.     }
  779. #ifdef PS
  780.     else if (psfile) {
  781.       DrawColor(colcur);
  782.       fprintf(psdata, "%d %d %d %d rf\n",
  783.         MAX(x1-metawid/4, 0), MAX(y1-metawid/4, 0),
  784.         x2-x1+metawid/4, y2-y1+metawid/4);
  785.     }
  786. #endif
  787. #ifdef META
  788.     else {
  789.       metafilldes = colcur;
  790.       MetaSelect();
  791.       MetaRectangle(x1-metawid/2, y1-metawid/2, x2+metawid/2, y2+metawid/2);
  792.     }
  793. #endif
  794.   }
  795. #ifdef X11
  796.   else
  797.     XFillRectangle(disp, pixmap, gc, x1, y1, x2-x1, y2-y1);
  798. #endif
  799. #ifdef MSG
  800.   else
  801.     _rectangle(_GFILLINTERIOR,
  802.       offsetx + x1, offsety + y1, offsetx + x2, offsety + y2);
  803. #endif
  804. }
  805.  
  806.  
  807. /* Draw a rectangle on the screen with specified thickness. This is just   */
  808. /* like DrawBlock() except that we are only drawing the edges of the area. */
  809.  
  810. void DrawBox(x1, y1, x2, y2, xsiz, ysiz)
  811. int x1, y1, x2, y2, xsiz, ysiz;
  812. {
  813. #ifdef META
  814.   if (metafile)
  815.     /* For thin boxes in metafiles, we can just output one rectangle record */
  816.     /* instead of drawing each side separately as we have to do otherwise.  */
  817.     if (xsiz <= 1 && ysiz <= 1) {
  818.       metafilldes = 16;              /* Specify a hollow fill brush. */
  819.       MetaSelect();
  820.       MetaRectangle(x1, y1, x2, y2);
  821.       return;
  822.     }
  823. #endif
  824.   DrawBlock(x1, y1, x2, y1 + ysiz - 1);
  825.   DrawBlock(x1, y1 + ysiz, x1 + xsiz - 1, y2 - ysiz);
  826.   DrawBlock(x2 - xsiz + 1, y1 + ysiz, x2, y2 - ysiz);
  827.   DrawBlock(x1, y2 - ysiz + 1, x2, y2);
  828. }
  829.  
  830.  
  831. /* Clear and erase the graphics screen or bitmap contents. */
  832.  
  833. void DrawClearScreen()
  834. {
  835. #ifdef PS
  836.   if (psfile) {
  837.     /* For PostScript charts first output page orientation information. */
  838.     if (!epsfile) {
  839.       if (psinchz == 0)
  840.         psinchz = chartx > charty ? -1 : 1;
  841.       if (psinchz < 0) {
  842.         /* Chartx and charty are reversed for Landscape mode. */
  843.         fprintf(psdata, "%d %d translate\n",
  844.           ((int)(psinchx*72.0+ROUND) + charty)/2,
  845.           ((int)(psinchy*72.0+ROUND) + chartx)/2);
  846.         fprintf(psdata, "-90 rotate\n");
  847.       } else {
  848.         /* Most charts are in Portrait mode */
  849.         fprintf(psdata, "%d %d translate\n",
  850.           ((int)(psinchx*72.0+ROUND) - chartx)/2,
  851.           ((int)(psinchy*72.0+ROUND) + charty)/2);
  852.       }
  853.     } else
  854.       fprintf(psdata, "0 %d translate\n", charty);
  855.     fprintf(psdata, "1 -1 scale\n");
  856.     scale *= PSMUL; chartx *= PSMUL; charty *= PSMUL;
  857.     fprintf(psdata, "1 %d div dup scale\n", PSMUL);
  858.   }
  859. #endif
  860. #ifdef META
  861.   if (metafile)
  862.     MetaInit();    /* For metafiles first go write our header information. */
  863. #endif
  864.  
  865.   /* Hack: If a comparison relationship chart is set and we're in the -Z  */
  866.   /* horizon or -S space graphics chart modes (which normally is just the */
  867.   /* same as single chart graphics) don't actually clear the screen.      */
  868.  
  869.   if (relation <= DASHr0 && xnow > 0 && (modex == MODEZ || modex == MODES))
  870.     return;
  871. #ifdef MSG
  872.   if (!xfile)
  873.     _clearscreen(_GCLEARSCREEN);
  874. #endif
  875.   DrawColor(off);
  876.   DrawBlock(0, 0, chartx - 1, charty - 1);    /* Clear bitmap screen. */
  877. }
  878.  
  879.  
  880. /* Draw a line on the screen, specified by its endpoints. In addition, we */
  881. /* have specified a skip factor, which allows us to draw dashed lines.    */
  882.  
  883. void DrawDash(x1, y1, x2, y2, skip)
  884. int x1, y1, x2, y2, skip;
  885. {
  886. #ifdef META
  887.   static word PTR poly;
  888. #endif
  889.   int x = x1, y = y1, xadd, yadd, yinc, xabs, yabs, i, j = 0;
  890.  
  891.   if (skip < 0)
  892.     skip = 0;
  893. #ifdef ISG
  894.   if (!xfile) {
  895.     if (!skip) {
  896. #ifdef X11
  897.       /* For non-dashed X window lines, let's have the Xlib do it for us. */
  898.  
  899.       XDrawLine(disp, pixmap, gc, x1, y1, x2, y2);
  900. #else
  901.       /* For non-dashed lines, let's have the graphics library do it for us. */
  902.  
  903.       _moveto(offsetx + x1, offsety + y1);
  904.       _lineto(offsetx + x2, offsety + y2);
  905. #endif
  906.       return;
  907.     }
  908.   }
  909. #endif /* ISG */
  910.  
  911. #ifdef PS
  912.   if (psfile) {
  913.  
  914.     /* For PostScript charts we can save file size if we output a LineTo  */
  915.     /* command when the start vertex is the same as the end vertex of the */
  916.     /* previous line drawn, instead of writing out both vertices.         */
  917.  
  918.     PSlinecap(TRUE);
  919.     PSdash(skip);
  920.     if (xpen != x1 || ypen != y1)
  921.       fprintf(psdata, "%d %d %d %d l\n", x1, y1, x2, y2);
  922.     else
  923.       fprintf(psdata, "%d %d t\n", x2, y2);
  924.     xpen = x2; ypen = y2;
  925.     PSstroke(2);
  926.     return;
  927.   }
  928. #endif
  929. #ifdef META
  930.   if (metafile) {
  931.  
  932.     /* For metafile charts we can really save file size for consecutive */
  933.     /* lines sharing endpoints by consolidating them into a PolyLine.   */
  934.  
  935.     if (xpen != x1 || ypen != y1) {
  936.       metalinedes = (metalinedes & 15) + 16*(skip > 3 ? 3 : skip);
  937.       MetaSelect();
  938.       poly = metacur;
  939.       MetaRecord(8, 0x325);    /* Polyline */
  940.       MetaWord(2); MetaWord(x1); MetaWord(y1);
  941.     } else {
  942.       *poly += 2;
  943.       (*(poly+3))++;
  944.       /* Note: We should technically update the max record size in the   */
  945.       /* file header if need be here too, but it doesn't seem necessary. */
  946.     }
  947.     MetaWord(x2); MetaWord(y2);
  948.     xpen = x2; ypen = y2;
  949.     return;
  950.   }
  951. #endif
  952.  
  953.   /* If none of the above cases hold, we have to draw the line dot by dot. */
  954.  
  955.   xadd = x2 - x1 >= 0 ? 1 : 3;
  956.   yadd = y2 - y1 >= 0 ? 2 : 4;
  957.   xabs = abs(x2 - x1);
  958.   yabs = abs(y2 - y1);
  959.  
  960.   /* Technically what we're doing here is drawing a line which is more    */
  961.   /* horizontal then vertical. We always increment x by 1, and increment  */
  962.   /* y whenever a fractional variable passes a certain amount. For lines  */
  963.   /* that are more vertical than horizontal, we just swap x and y coords. */
  964.  
  965.   if (xabs < yabs) {
  966.     SWAP(xadd, yadd);
  967.     SWAP(xabs, yabs);
  968.   }
  969.   yinc = (xabs >> 1) - ((xabs & 1 ^ 1) && xadd > 2);
  970.   for (i = xabs+1; i; i--) {
  971.     if (j < 1)
  972.       DrawPoint(x, y);
  973.     j = j < skip ? j+1 : 0;
  974.     switch (xadd) {
  975.     case 1: x++; break;
  976.     case 2: y++; break;
  977.     case 3: x--; break;
  978.     case 4: y--; break;
  979.     }
  980.     yinc += yabs;
  981.     if (yinc - xabs >= 0) {
  982.       yinc -= xabs;
  983.       switch (yadd) {
  984.       case 1: x++; break;
  985.       case 2: y++; break;
  986.       case 3: x--; break;
  987.       case 4: y--; break;
  988.       }
  989.     }
  990.   }
  991. }
  992.  
  993.  
  994. /* Draw a normal line on the screen; however, if the x coordinates are close */
  995. /* to either of the two given bounds, then we assume that the line runs off  */
  996. /* one side and reappears on the other, so draw the appropriate two lines    */
  997. /* instead. This is used by the Ley line and astro-graph routines, which     */
  998. /* draw lines running around the world and hence off the edges of the maps.  */
  999.  
  1000. void DrawWrap(x1, y1, x2, y2, xmin, xmax)
  1001. int x1, y1, x2, y2;
  1002. {
  1003.   int xmid, ymid, i;
  1004.  
  1005.   if (x1 < 0) {           /* Special case for drawing world map. */
  1006.     DrawPoint(x2, y2);
  1007.     return;
  1008.   }
  1009.   xmid = (xmax-xmin) / 2;
  1010.  
  1011.   /* If endpoints aren't near opposite edges, just draw the line and return. */
  1012.  
  1013.   if (abs(x2-x1) < xmid) {
  1014.     DrawLine(x1, y1, x2, y2);
  1015.     return;
  1016.   }
  1017.   i = (xmax-xmin+1) + (x1 < xmid ? x1-x2 : x2-x1);
  1018.  
  1019.   /* Determine vertical coordinate where our line runs off edges of screen. */
  1020.  
  1021.   ymid = y1+(int)((real)(y2-y1)*
  1022.     (x1 < xmid ? (real)(x1-xmin) : (real)(xmax-x1))/(real)i + ROUND);
  1023.   DrawLine(x1, y1, x1 < xmid ? xmin : xmax, ymid);
  1024.   DrawLine(x2 < xmid ? xmin : xmax, ymid, x2, y2);
  1025. }
  1026.  
  1027.  
  1028. /* This routine, and its companion below, clips a line defined by its  */
  1029. /* endpoints to either above some line y=c, or below some line y=c. By */
  1030. /* passing in parameters in different orders, we can clip to vertical  */
  1031. /* lines, too. These are used by the DrawClip() routine below.         */
  1032.  
  1033. void ClipLesser(x1, y1, x2, y2, s)
  1034. int *x1, *y1, *x2, *y2, s;
  1035. {
  1036.   *x1 -= (int)((long)(*y1-s)*(*x2-*x1)/(*y2-*y1));
  1037.   *y1 = s;
  1038. }
  1039.  
  1040. void ClipGreater(x1, y1, x2, y2, s)
  1041. int *x1, *y1, *x2, *y2, s;
  1042. {
  1043.   *x1 += (int)((long)(s-*y1)*(*x2-*x1)/(*y2-*y1));
  1044.   *y1 = s;
  1045. }
  1046.  
  1047.  
  1048. /* Draw a line on the screen. This is just like DrawLine() routine earlier; */
  1049. /* however, first clip the endpoints to the window viewport before drawing. */
  1050.  
  1051. void DrawClip(x1, y1, x2, y2, xl, yl, xh, yh, skip)
  1052. int x1, y1, x2, y2, xl, yl, xh, yh, skip;
  1053. {
  1054.   if (x1 < xl)
  1055.     ClipLesser (&y1, &x1, &y2, &x2, xl);    /* Check left side of window. */
  1056.   if (x2 < xl)
  1057.     ClipLesser (&y2, &x2, &y1, &x1, xl);
  1058.   if (y1 < yl)
  1059.     ClipLesser (&x1, &y1, &x2, &y2, yl);    /* Check top side of window.  */
  1060.   if (y2 < yl)
  1061.     ClipLesser (&x2, &y2, &x1, &y1, yl);
  1062.   if (x1 > xh)
  1063.     ClipGreater(&y1, &x1, &y2, &x2, xh);    /* Check right of window.  */
  1064.   if (x2 > xh)
  1065.     ClipGreater(&y2, &x2, &y1, &x1, xh);
  1066.   if (y1 > yh)
  1067.     ClipGreater(&x1, &y1, &x2, &y2, yh);    /* Check bottom of window. */
  1068.   if (y2 > yh)
  1069.     ClipGreater(&x2, &y2, &x1, &y1, yh);
  1070.   DrawDash(x1, y1, x2, y2, skip);           /* Go draw the line.       */
  1071. }
  1072.  
  1073.  
  1074. /* Draw a circle or ellipse inside the given bounding rectangle. */
  1075.  
  1076. void DrawEllipse(x1, y1, x2, y2)
  1077. int x1, y1, x2, y2;
  1078. {
  1079.   int x, y, rx, ry, m, n, u, v, i;
  1080.  
  1081.   if (xfile) {
  1082.     x = (x1+x2)/2; y = (y1+y2)/2; rx = (x2-x1)/2; ry = (y2-y1)/2;
  1083.     if (xbitmap) {
  1084.       InitCircle();
  1085.       m = x + rx; n = y;
  1086.       for (i = 0; i <= DEGD; i += DEGINC) {
  1087.         u = x + (int)((real)rx*circ->x[i]); v = y + (int)((real)ry*circ->y[i]);
  1088.         u = MIN(u, x + rx-1); v = MIN(v, y + ry-1);
  1089.         DrawLine(m, n, u, v);
  1090.         m = u; n = v;
  1091.       }
  1092.     }
  1093. #ifdef PS
  1094.     else if (psfile) {
  1095.       PSlinecap(FALSE);
  1096.       PSforcestroke();
  1097.       PSdash(0);
  1098.       fprintf(psdata, "%d %d %d %d el\n", rx, ry, x, y);
  1099.     }
  1100. #endif
  1101. #ifdef META
  1102.     else {
  1103.       metafilldes = 16;    /* Specify a hollow fill brush. */
  1104.       MetaSelect();
  1105.       MetaEllipse(x1+metawid/3, y1+metawid/3, x2+metawid/3, y2+metawid/3);
  1106.     }
  1107. #endif
  1108.   }
  1109. #ifdef X11
  1110.   else
  1111.     XDrawArc(disp, pixmap, gc, x1, y1, x2-x1, y2-y1, 0, 360*64);
  1112. #endif
  1113. #ifdef MSG
  1114.   else
  1115.     _ellipse(_GBORDER, offsetx + x1, offsety + y1, offsetx + x2, offsety + y2);
  1116. #endif
  1117. }
  1118.  
  1119.  
  1120. /* Print a string of text on the graphic window at specified location. To  */
  1121. /* do this we either use Astrolog's own "font" (6x10) and draw each letter */
  1122. /* separately, or else specify system fonts for PostScript and metafiles.  */
  1123.  
  1124. void DrawText(string, x, y, base)
  1125. char *string;
  1126. int x, y, base;
  1127. {
  1128.   int s = scale, c = colcur, len;
  1129.  
  1130.   len = StringLen(string);
  1131.   scale = 100 * scalet;
  1132.   x += SCALE;
  1133.   if (base >= FALSE)
  1134.     x -= len*FONTX*SCALE/2;
  1135.   if (!base)
  1136.     y -= FONTY*SCALE/2;
  1137.   else
  1138.     y -= (FONTY-3)*SCALE;
  1139.   DrawColor(off);
  1140.   DrawBlock(x, y, x+FONTX*SCALE*len, y+(FONTY-1)*SCALE);
  1141.   DrawColor(c);
  1142. #ifdef PS
  1143.   if (psfile && xfont) {
  1144.     PSfont(4);
  1145.     fprintf(psdata, "%d %d(%s)center\n",
  1146.       x + FONTX*SCALE*len/2, y + FONTY*SCALE/2, string);
  1147.     scale = s;
  1148.     return;
  1149.   }
  1150. #endif
  1151.   while (*string) {
  1152. #ifdef META
  1153.     if (metafile && xfont) {
  1154.       metafontdes = 3;
  1155.       metatxtcdes = colcur;
  1156.       metatxtades = 0x6 | 0 /* Center | Top */;
  1157.       MetaSelect();
  1158.       MetaTextOut(x, y, 1);
  1159.       MetaWord(MAKEWORD(*string, 0));
  1160.     } else
  1161. #endif
  1162.       DrawTurtle(asciidraw[*string-' '], x, y);
  1163.     x += FONTX*SCALE;
  1164.     string++;
  1165.   }
  1166.   scale = s;
  1167. }
  1168.  
  1169.  
  1170. /* Draw the glyph of a sign at particular coordinates on the screen.    */
  1171. /* To do this we either use Astrolog's turtle vector representation or  */
  1172. /* we may specify a system font character for PostScript and metafiles. */
  1173.  
  1174. void DrawSign(i, x, y)
  1175. int i, x, y;
  1176. {
  1177. #ifdef PS
  1178.   if (psfile && xfont) {
  1179.     PSfont(1);
  1180.     fprintf(psdata, "%d %d(%c)center\n", x, y, 'A' + i - 1);
  1181.     return;
  1182.   }
  1183. #endif
  1184. #ifdef META
  1185.   if (metafile && xfont) {
  1186.     metafontdes = 1;
  1187.     metatxtcdes = colcur;
  1188.     metatxtades = 0x6 | 0x8 /* Center | Bottom */;
  1189.     MetaSelect();
  1190.     MetaTextOut(x, y+4*SCALE, 1);
  1191.     MetaWord(MAKEWORD('^' + i - 1, 0));
  1192.     return;
  1193.   }
  1194. #endif
  1195.   DrawTurtle(signdraw[i], x, y);
  1196. }
  1197.  
  1198.  
  1199. /* Draw the number of a house at particular coordinates on the screen. */
  1200. /* We either use a turtle vector or write a number in a system font.   */
  1201.  
  1202. void DrawHouse(i, x, y)
  1203. int i, x, y;
  1204. {
  1205. #ifdef PS
  1206.   if (psfile && xfont) {
  1207.     PSfont(3);
  1208.       fprintf(psdata, "%d %d(%d)center\n", x, y, i);
  1209.     return;
  1210.   }
  1211. #endif
  1212. #ifdef META
  1213.   if (metafile && xfont) {
  1214.     metafontdes = 2;
  1215.     metatxtcdes = colcur;
  1216.     metatxtades = 0x6 | 0x8 /* Center | Bottom */;
  1217.     MetaSelect();
  1218.     MetaTextOut(x, y+3*SCALE, 1 + (i>9));
  1219.     MetaWord(MAKEWORD(i > 9 ? '1' : '0'+i, i > 9 ? '0'+i-10 : 0));
  1220.     return;
  1221.   }
  1222. #endif
  1223.   DrawTurtle(housedraw[i], x, y);
  1224. }
  1225.  
  1226.  
  1227. /* Draw the glyph of an object at particular coordinates on the screen. */
  1228.  
  1229. void DrawObject(i, x, y)
  1230. int i, x, y;
  1231. {
  1232.   char glyph[4];
  1233. #ifdef PS
  1234.   static char objectchar[] = "dQRSTUVWXYZ     < ba ";
  1235. #endif
  1236. #ifdef META
  1237.   char c = 0;
  1238. #endif
  1239.  
  1240.   if (!xlabel)    /* If we are inhibiting labels, then do nothing. */
  1241.     return;
  1242.  
  1243.   /* For other planet centered charts, we have to remember that that     */
  1244.   /* particular planet's index now represents the Earth. If we are given */
  1245.   /* that index to draw, then change it so we draw the Earth instead.    */
  1246.  
  1247.   if (modex != MODES &&
  1248.     ((i == centerplanet && i > _MOO) || (centerplanet == 0 && i == _SUN)))
  1249.     i = 0;
  1250.   DrawColor(objectcolor[i]);
  1251.   if (i <= BASE) {
  1252. #ifdef PS
  1253.     if (psfile && xfont == 1 && i <= OBJECTS && objectchar[i] != ' ') {
  1254.       PSfont(2);
  1255.       fprintf(psdata, "%d %d(%c)center\n", x, y, objectchar[i]);
  1256.       return;
  1257.     }
  1258. #endif
  1259. #ifdef META
  1260.     if (metafile && xfont == 1) {
  1261.       if (i < _SUN)
  1262.         c = ';';
  1263.       else if (i <= _PLU) c = 'Q' + i - 1;
  1264.       else if (i == _NOD) c = '<';
  1265.       else if (i == _MC)  c = 'b';
  1266.       else if (i == _ASC) c = 'a';
  1267.     }
  1268.     if (c) {
  1269.       metafontdes = 4;
  1270.       metatxtcdes = colcur;
  1271.       metatxtades = 0x6 | 0x8 /* Center | Bottom */;
  1272.       MetaSelect();
  1273.       MetaTextOut(x, y+5*SCALE, 1);
  1274.       MetaWord(MAKEWORD(c, 0));
  1275.       return;
  1276.     }
  1277. #endif
  1278.     DrawTurtle(objectdraw[i], x, y);
  1279.  
  1280.   /* Normally we can just go draw the glyph; however, stars don't have */
  1281.   /* glyphs, so for these draw their three letter abbreviation.        */
  1282.  
  1283.   } else {
  1284.     sprintf(glyph, "%c%c%c", OBJNAM(i));
  1285.     DrawText(glyph, x, y, FALSE);
  1286.   }
  1287. }
  1288.  
  1289.  
  1290. /* Draw the glyph of an aspect at particular coordinates on the screen. */
  1291. /* Again we either use Astrolog's turtle vector or a system Astro font. */
  1292.  
  1293. void DrawAspect(i, x, y)
  1294. int i, x, y;
  1295. {
  1296. #ifdef PS
  1297.   static char aspectchar[] = "!\"#$'&%()+-       ";
  1298. #endif
  1299. #ifdef META
  1300.   char c = 0;
  1301. #endif
  1302.  
  1303. #ifdef PS
  1304.   if (psfile && xfont == 1 && aspectchar[i-1] != ' ') {
  1305.     PSfont(2);
  1306.     fprintf(psdata, "%d %d(%s%c)center\n", x, y,
  1307.       i == _SSQ || i == _SES ? "\\" : "", aspectchar[i-1]);
  1308.     return;
  1309.   }
  1310. #endif
  1311. #ifdef META
  1312.   if (metafile && xfont == 1) {
  1313.     if (i <= _TRI)
  1314.       c = '!' + i - 1;
  1315.     else if (i == _SEX) c = '\'';
  1316.     else if (i == _INC) c = '&';
  1317.     else if (i == _SSX) c = '%';
  1318.     else if (i == _SSQ) c = '(';
  1319.     else if (i == _SES) c = ')';
  1320.     else if (i == _QUI) c = '+';
  1321.     else if (i == _BQN) c = '-';
  1322.   }
  1323.   if (c) {
  1324.     metafontdes = 4;
  1325.     metatxtcdes = colcur;
  1326.     metatxtades = 0x6 | 0x8 /* Center | Bottom */;
  1327.     MetaSelect();
  1328.     MetaTextOut(x, y+5*SCALE, 1);
  1329.     MetaWord(MAKEWORD(c, 0));
  1330.     return;
  1331.   }
  1332. #endif
  1333.   DrawTurtle(aspectdraw[i], x, y);
  1334. }
  1335.  
  1336.  
  1337. /* Convert a string segment to a positive number, updating the string to  */
  1338. /* point beyond the number chars. Return 1 if the string doesn't point to */
  1339. /* a numeric value. This is used by the DrawTurtle() routine to extract   */
  1340. /* motion vector quantities from draw strings, e.g. the "12" in "U12".    */
  1341.  
  1342. int IntInString(str)
  1343. char **str;
  1344. {
  1345.   int num = 0, i = 0;
  1346.  
  1347.   loop {
  1348.     if (**str < '0' || **str > '9')
  1349.       return num > 0 ? num : (i < 1 ? 1 : 0);
  1350.     num = num*10+(**str)-'0';
  1351.     (*str)++;
  1352.     i++;
  1353.   }
  1354. }
  1355.  
  1356.  
  1357. /* This routine is used to draw complicated objects composed of lots of line */
  1358. /* segments on the screen, such as all the glyphs and coastline pieces. It   */
  1359. /* is passed in a string of commands defining what to draw in relative       */
  1360. /* coordinates. This is a copy of the format of the BASIC draw command found */
  1361. /* in PC's. For example, "U5R10D5L10" means go up 5 dots, right 10, down 5,  */
  1362. /* and left 10 - draw a box twice as wide as it is high.                     */
  1363.  
  1364. void DrawTurtle(lin, x0, y0)
  1365. char *lin;
  1366. int x0, y0;
  1367. {
  1368.   int i, j, x, y, deltax, deltay, blank, noupdate;
  1369.   char cmd;
  1370.  
  1371.   turtlex = x0; turtley = y0;
  1372.   while (cmd = CAP(*lin)) {
  1373.     lin++;
  1374.  
  1375.     /* 'B' prefixing a command means just move the cursor, and don't draw. */
  1376.  
  1377.     if (blank = cmd == 'B') {
  1378.       cmd = CAP(*lin);
  1379.       lin++;
  1380.     }
  1381.  
  1382.     /* 'N' prefixing a command means don't update cursor when done drawing. */
  1383.  
  1384.     if (noupdate = cmd == 'N') {
  1385.       cmd = CAP(*lin);
  1386.       lin++;
  1387.     }
  1388.  
  1389.     /* Here we process the eight directional commands. */
  1390.  
  1391.     switch (cmd) {
  1392.     case 'U': deltax =  0; deltay = -1; break;      /* Up    */
  1393.     case 'D': deltax =  0; deltay =  1; break;      /* Down  */
  1394.     case 'L': deltax = -1; deltay =  0; break;      /* Left  */
  1395.     case 'R': deltax =  1; deltay =  0; break;      /* Right */
  1396.     case 'E': deltax =  1; deltay = -1; break;      /* NorthEast */
  1397.     case 'F': deltax =  1; deltay =  1; break;      /* SouthEast */
  1398.     case 'G': deltax = -1; deltay =  1; break;      /* SouthWest */
  1399.     case 'H': deltax = -1; deltay = -1; break;      /* NorthWest */
  1400.     default: PrintError("Bad turtle subcommand.");  /* Shouldn't happen. */
  1401.     }
  1402.     x = turtlex;
  1403.     y = turtley;
  1404.     j = IntInString(&lin)*SCALE;    /* Figure out how far to draw. */
  1405.     if (blank) {
  1406.       turtlex += deltax*j;
  1407.       turtley += deltay*j;
  1408.     } else {
  1409.       if (psfile || metafile) {
  1410.         turtlex += deltax*j;
  1411.         turtley += deltay*j;
  1412.         DrawLine(x, y, turtlex, turtley);
  1413.       } else {
  1414.         DrawPoint(turtlex, turtley);
  1415.         for (i = 0; i < j; i++) {
  1416.           turtlex += deltax;
  1417.           turtley += deltay;
  1418.           DrawPoint(turtlex, turtley);
  1419.         }
  1420.       }
  1421.       if (noupdate) {
  1422.         turtlex = x;
  1423.         turtley = y;
  1424.       }
  1425.     }
  1426.   }
  1427. }
  1428. #endif /* GRAPH */
  1429.  
  1430. /* xgeneral.c */
  1431.